package com.sencorsta.ids.core.tcp.socket.server;

import com.sencorsta.ids.core.configure.SysConfig;
import com.sencorsta.ids.core.log.Out;
import com.sencorsta.ids.core.tcp.socket.coder.RpcDecoder;
import com.sencorsta.ids.core.tcp.socket.coder.RpcEncoder;
import com.sencorsta.utils.string.StringUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.ThreadPerTaskExecutor;

import java.net.BindException;
import java.net.InetSocketAddress;

/**
 * 服务端网络服务引导
 *
 * @author ICe
 */
public class RpcServerBootstrap {

	private String name;
	private ServerBootstrap serverBootstrap;

	private ChannelInitializer<SocketChannel> factory;

	private EventLoopGroup bossGroup;
	private EventLoopGroup workerGroup;

	//private static final int PARENTGROUP_SIZE = SysConfig.getInstance().getInt("netty.server.parentgroup.size", 2);
	//private static final int CHILDGROUP_SIZE = SysConfig.getInstance().getInt("netty.server.childgroup.size", 4);

	protected String hostStr = "service.host";
	protected String hostPublicStr="service.host.public";
	protected String minPortStr = "service.port.min";
	protected String maxPortStr = "service.port.max";
	protected String portStr = "service.port";


	public RpcServerBootstrap(String name) {
		this.name = name;
		this.factory = new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ChannelPipeline pipeline = ch.pipeline();
				pipeline.addLast("decoder", new RpcDecoder());
				pipeline.addLast("encoder", new RpcEncoder());
				pipeline.addLast("handler", new RpcChannelHandler());
			}
		};
	}


	public RpcServerBootstrap(String name, ChannelInitializer<SocketChannel> factory) {
		this.name = name;
		this.factory = factory;
	}

	/**
	 * 调用此方法绑定端口侦听服务，添加编解码过滤器
	 *
	 * @throws Exception
	 */
	boolean isEnable = false;
	public void start() throws Exception {
		try {
			serverBootstrap = new ServerBootstrap();

//			if (GSystem.isLinux()) {
//				bossGroup = new EpollEventLoopGroup();
//				workerGroup = new EpollEventLoopGroup();
//				serverBootstrap.channel(EpollServerSocketChannel.class);
//			} else {
//				bossGroup = new NioEventLoopGroup();
//				workerGroup = new NioEventLoopGroup();
//				serverBootstrap.channel(NioServerSocketChannel.class);
//			}

			bossGroup = new NioEventLoopGroup(0,new ThreadPerTaskExecutor(new DefaultThreadFactory(name+"Boss")));
			workerGroup = new NioEventLoopGroup(0,new ThreadPerTaskExecutor(new DefaultThreadFactory(name+"Worker")));
			serverBootstrap.channel(NioServerSocketChannel.class);

			serverBootstrap.group(bossGroup, workerGroup);

			serverBootstrap.childHandler(factory);

			serverBootstrap
					// .option(ChannelOption.SO_BACKLOG, 128)
					.option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_RCVBUF, 512)
					.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
					.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
					.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
					// .childOption(ChannelOption.RCVBUF_ALLOCATOR,
					// AdaptiveRecvByteBufAllocator.DEFAULT)
					.childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true);

			isEnable = false;
			String ip = SysConfig.getInstance().get(hostStr,"0.0.0.0");
			int serviceMin = SysConfig.getInstance().getInt(minPortStr);
			int serviceMax = SysConfig.getInstance().getInt(maxPortStr);
			for (int j = serviceMin; j <= serviceMax; j++) {
				ChannelFuture f = null;
				try {
					InetSocketAddress socketAddress = (ip != null && ip.length() > 6) ? new InetSocketAddress(ip, j)
							: new InetSocketAddress(j);
					f = serverBootstrap.bind(socketAddress).sync();
					if (f.isSuccess()) {
						Out.trace(name + "端口可用:", " -> ", j);
						String realHost=SysConfig.getInstance().get(hostPublicStr);
						if (StringUtil.isEmpty(SysConfig.getInstance().get(hostPublicStr,null))){
							Out.info(name + "服务绑定于 -> " + socketAddress);
						}else {
							realHost+=":"+j;
							Out.info(name + "服务绑定于 -> " + socketAddress +" (公开地址:"+realHost+")");
						}
						SysConfig.getInstance().put(portStr, j + "");
						isEnable = true;
						break;
					}
				} catch (Exception e) {
					Out.trace("端口", j, "已被占用,查找新的" + "[" + serviceMin + "-" + serviceMax + "]" + "端口中...");
				}
			}
			if (isEnable == false) {
				throw new Exception(name + "服务器没有找到可用内部端口!,请检查配置是否正确! ->" +ip+ "[" + serviceMin + "-" + serviceMax + "]");
			}
		} catch (Exception e) {
			throw e;
		}
	}

	public boolean isStarted(){
		return isEnable;
	}



}
